import {
  Component,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  ElementRef,
  ChangeDetectorRef,
  OnInit,
  OnDestroy,
} from '@angular/core';
import { trigger, state, style, transition, animate, AnimationEvent, group } from '@angular/animations';
import { Subject } from 'rxjs';
import { TypeUtils } from '../core/utils/type';
import { NotificationOptions } from './interface';
import { NotificationContainerService } from './notification.container.service';

@Component({
  selector: 'lv-notification',
  exportAs: 'lvNotification',
  templateUrl: './notification.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    '[style.display]': "'block'",
  },
  animations: [
    trigger('notificationAnim', [
      state(
        'leftIn',
        style({
          transform: 'translateX(0)',
          opacity: 1,
        }),
      ),
      state(
        'leftOut',
        style({
          transform: 'translateX(-10%)',
          opacity: 0,
        }),
      ),
      state(
        'rightIn',
        style({
          transform: 'translateX(0)',
          opacity: 1,
        }),
      ),
      state(
        'rightOut',
        style({
          transform: 'translateX(150px)',
          opacity: 0,
        }),
      ),
      transition('leftOut => leftIn', [
        group([
          animate(
            '250ms cubic-bezier(0, 0, 0.2, 1)',
            style({
              transform: 'translateX(0)',
            }),
          ),
          animate(
            '250ms cubic-bezier(0.33, 0, 0.67, 1)',
            style({
              opacity: 1,
            }),
          ),
        ]),
      ]),
      transition('leftIn => leftOut', [
        group([
          animate(
            '200ms cubic-bezier(0.33, 0, 0.67, 1)',
            style({
              opacity: 0,
            }),
          ),
        ]),
      ]),
      transition('rightOut => rightIn', [
        group([
          animate(
            '250ms cubic-bezier(0, 0, 0.2, 1)',
            style({
              transform: 'translateX(0)',
            }),
          ),
          animate(
            '250ms cubic-bezier(0.33, 0, 0.67, 1)',
            style({
              opacity: 1,
            }),
          ),
        ]),
      ]),
      transition('rightIn => rightOut', [
        group([
          animate(
            '200ms cubic-bezier(0.33, 0, 0.67, 1)',
            style({
              opacity: 0,
            }),
          ),
        ]),
      ]),
    ]),
  ],
})
export class NotificationComponent implements OnInit, OnDestroy {
  @Input() lvOptions;

  public animationState;
  public typeUtils = TypeUtils;
  public options: NotificationOptions;
  public showState$ = new Subject<boolean>();
  private timer = null;

  constructor(
    public cdr: ChangeDetectorRef,
    public el: ElementRef,
    public notificationContainer: NotificationContainerService,
  ) {}

  mouseLeave(): void {
    this.startTiming();
  }

  mouseEnter(): void {
    if (this.options.lvKeepShowOnHover) {
      this.clearTimer();
    }
  }

  ngOnDestroy(): void {
    this.clearTimer();
    this.lvOptions.options.lvOnDestroy?.();
  }

  close(): void {
    this.setAnimationState('hide');
    this.cdr.markForCheck();
    const opensubscribe = this.showState$.subscribe((show): void => {
      if (!show) {
        opensubscribe.unsubscribe();
        this.notificationContainer.remove(this.cdr, this.lvOptions.id);
      }
    });
  }

  ngOnInit(): void {
    this.options = this.lvOptions.options;
    this.el.nativeElement.style.transform = `translate(${this.options.lvOffset.x}px, ${this.options.lvOffset.y}px)`;

    // Initialize animatin status
    this.setAnimationState('hide');
    setTimeout((): void => {
      this.setAnimationState('show');
      this.startTiming();
      this.cdr.markForCheck();
    }, 0);
  }

  /**
   * listen animation(done)
   */
  public onAnimationDone(event: AnimationEvent): void {
    const flag1 =
      event.triggerName === 'notificationAnim' &&
      event.fromState !== 'void' &&
      (event.toState === 'leftIn' || event.toState === 'rightIn');
    const flag2 =
      event.triggerName === 'notificationAnim' &&
      ((event.fromState === 'leftIn' && event.toState === 'leftOut') ||
        (event.fromState === 'rightIn' && event.toState === 'rightOut'));
    if (flag1) {
      this.showState$.next(true);
    }
    if (flag2) {
      this.showState$.next(false);
    }
  }

  private clearTimer(): void {
    if (this.timer !== null) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }

  private startTiming(): void {
    const dur = this.options.lvDuration;
    if (dur === 0) {
      return;
    }

    this.timer = setTimeout((): void => {
      this.close();
    }, dur);
  }

  private setAnimationState(_state): void {
    if (_state === 'show') {
      (this.options.lvPosition === 'topLeft' || this.options.lvPosition === 'bottomLeft') &&
        (this.animationState = 'leftIn');
      (this.options.lvPosition === 'topRight' || this.options.lvPosition === 'bottomRight') &&
        (this.animationState = 'rightIn');
    } else if (_state === 'hide') {
      (this.options.lvPosition === 'topLeft' || this.options.lvPosition === 'bottomLeft') &&
        (this.animationState = 'leftOut');
      (this.options.lvPosition === 'topRight' || this.options.lvPosition === 'bottomRight') &&
        (this.animationState = 'rightOut');
    }
  }
}
